/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF.  The full HDF copyright notice, including       *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/****************************************************************************
 * tdatainfo.c - tests the functions VSgetdatainfo, ANgetdatainfo,
 *			GRgetdatainfo, and Hgetntinfo
 * Structure of the file:
 *    test_datainfo - test driver
 *	test_simple_vs   - test VSgetdatainfo with data stored in one
 *			   contiguous block
 *	test_append_vs   - test VSgetdatainfo with data stored in linked blocks
 *	test_annotation  - test ANgetdatainfo
 *	test_oneblock_ri - test GRgetdatainfo with images stored in one
 *			   contiguous block, with and without compression
 *	test_dfr8_24     - test GRgetdatainfo with RI8 and RI24
 *	test_getntinfo   - test Hgetntinfo
 ****************************************************************************/

#include "hdf.h"
#include "tdatainfo.h"
#include "testhdf.h"

static void test_simple_vs();
static void test_append_vs();
static void test_annotation();
static void test_oneblock_ri();
static void test_dfr8_24();
static void test_getntinfo();
static void test_getpalinfo();

/***********************************************************************
  NOTE: At this time, some of the offsets in these tests are verified
        by hard-coding.  When adding new data to the files in exiting
        tests, please either add data at the bottom of the files or
        change the hard-coded offsets appropriately, using the values
        from the error messages.  However, you'll need to verify the
        correctness of these values first using the command 'od' on
        the hdf file.
*************************************************************************/

/* Structure to hold annotation datainfo temporarily */
typedef struct {
    char  anntext[100]; /* values of the annotation */
    int32 offset;       /* offset of data block */
    int32 length;       /* length of data block */
} t_ann_info_t;

/* Functions to access t_hdfdatainfo_t, defined in ./tdatainfo.h */

/* alloc_info is a utility function that allocates t_hdf_datainfo_t's members*/
int
alloc_info(t_hdf_datainfo_t *info, unsigned info_count)
{
    info->offsets = (int32 *)malloc(info_count * sizeof(int32));
    if (info->offsets == NULL)
        return -1;
    info->lengths = (int32 *)malloc(info_count * sizeof(int32));
    if (info->lengths == NULL)
        return -1;
    return 0;
}

void
free_info(t_hdf_datainfo_t *info)
{
    if (info != NULL) {
        free(info->offsets);
        free(info->lengths);
    }
}

/****************************************************************************
   Name: test_simple_vs() - tests non-special Vdatas

   Description:
        This routine creates and writes contiguous data to vdatas and verifies
        some data and data information with VSgetdatainfo.  The tests include
        the following vdatas:
        - a single field vdata with int32 values
        - a multiple field/multiple order vdata with all int32 values
        - a single field vdata with character values
        Only the character vdata's values are verified against the original
        buffer.  The other two were verified by examining the hdf file using
        od. (Todo 1)
   BMR - Jul 2010
 ****************************************************************************/
#define SIMPLE_FILE      "tdatainfo_simple.hdf" /* data file */
#define NONSPECIAL_VS    "Non-Special Vdata"
#define CONTCLASS_NAME   "Contiguous Vdata"
#define LINKED_BLOCK     "Linked-block Vdata"
#define FIELD1_NAME      "Field1" /* contains three integers */
#define FIELD2_NAME      "Field2" /* contains one integer */
#define FIELD3_NAME      "Field3" /* contains two integers */
#define FIELD_NAME_LIST  "Field1,Field2,Field3"
#define ORDER_1          3                             /* order of first field of 1st vdata */
#define ORDER_2          1                             /* order of second field of 1st vdata */
#define ORDER_3          2                             /* order of third field of 1st vdata */
#define BLOCK_SIZE       80                            /* arbitrary number for block size */
#define NUM_BLOCKS       8                             /* arbitrary number for number of blocks */
#define N_VALS_PER_REC_1 1                             /* # of values per record in single vdata */
#define N_VALS_PER_REC_2 (ORDER_1 + ORDER_2 + ORDER_3) /* # of vals/rec. in multiple vdata */
#define N_RECORDS                                                                                            \
    5 /* number of records to be written to the                                                              \
         vdatas at every write */
static void
test_simple_vs()
{
    int32 fid,                                  /* File ID */
        vsid,                                   /* Vdata ID */
        vs_ref,                                 /* Vdata ref# */
        n_records,                              /* number of records actually written to vdata */
        data_buf0[N_RECORDS][N_VALS_PER_REC_1], /* for single vdata's data */
        data_buf1[N_RECORDS][N_VALS_PER_REC_2]; /* for multiple vdata's data*/
    char  data_buf2[N_RECORDS] = {'H', '4', 'M', 'A', 'P'};
    int16 rec_num;        /* current record number */
    int32 offset, length; /* offset/length buffers for single block of data */
    int   n_blocks;       /* number of blocks a vdata has */
    int32 status;         /* Status values from routines */
    int   status_n;       /* Status values from routines */

    /* Open the HDF file and initialize the interface. */
    fid = Hopen(SIMPLE_FILE, DFACC_CREATE, 0);
    CHECK_VOID(fid, FAIL, "Hopen");
    status = Vstart(fid);
    CHECK_VOID(status, FAIL, "Vstart");

    /* Create and write to the first vdata using high-level func VHstoredata */
    for (rec_num = 0; rec_num < N_RECORDS; rec_num++)
        data_buf0[rec_num][0] = 5 + rec_num;

    vs_ref = VHstoredata(fid, "Only field", (const uint8 *)data_buf0, N_RECORDS, DFNT_INT32,
                         "One Field One Order", CONTCLASS_NAME);

    /*
     * Get access to the vdata just created then get and verify offset/length
     * of its data
     */

    vsid = VSattach(fid, vs_ref, "w");
    CHECK_VOID(vsid, FAIL, "VSattach");

    /* Get the number of data blocks first, should be 1 */
    n_blocks = VSgetdatainfo(vsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 1, "VSgetdatainfo");

    /* Get the offset/length of the data, should be 294/20 */
    n_blocks = VSgetdatainfo(vsid, 0, (unsigned)n_blocks, &offset, &length);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");

    /* Verify offset/length */
    VERIFY_VOID(offset, 294, "VSgetdatainfo offset from 'One Field One Order'");
    VERIFY_VOID(length, 20, "VSgetdatainfo length from 'One Field One Order'");

    status = VSdetach(vsid);
    CHECK_VOID(status, FAIL, "VSdetach");

    /*
     * Create a multi-field/multi-order vdata, named NONSPECIAL_VS, in class
     * CONTCLASS_NAME, write data to it, then get and verify its data's
     * offsets/lengths
     */
    vsid = VSattach(fid, -1, "w");
    CHECK_VOID(vsid, FAIL, "VSattach");

    /* Set name and class name of the vdata. */
    status = VSsetname(vsid, NONSPECIAL_VS);
    CHECK_VOID(status, FAIL, "VSsetname");
    status = VSsetclass(vsid, CONTCLASS_NAME);
    CHECK_VOID(status, FAIL, "VSsetclass");

    /* Record its reference number for later access before detaching it */
    vs_ref = VSQueryref(vsid);
    CHECK_VOID(vs_ref, FAIL, "VSQueryref");
    status = VSdetach(vsid);
    CHECK_VOID(status, FAIL, "VSdetach");

    /* Attach to vdata NONSPECIAL_VS to write data, but first verify that
        number of data blocks is 0 */
    vsid = VSattach(fid, vs_ref, "w");
    CHECK_VOID(vsid, FAIL, "VSattach");

    n_blocks = VSgetdatainfo(vsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 0, "VSgetdatainfo");

    /* Define the fields in the vdata */
    status_n = VSfdefine(vsid, FIELD1_NAME, DFNT_INT32, ORDER_1);
    CHECK_VOID(status_n, FAIL, "VSfdefine");
    status_n = VSfdefine(vsid, FIELD2_NAME, DFNT_INT32, ORDER_2);
    CHECK_VOID(status_n, FAIL, "VSfdefine");
    status_n = VSfdefine(vsid, FIELD3_NAME, DFNT_INT32, ORDER_3);
    CHECK_VOID(status_n, FAIL, "VSfdefine");
    status_n = VSsetfields(vsid, FIELD_NAME_LIST);
    CHECK_VOID(status_n, FAIL, "VSsetfields");

    /*
     * Buffer the data by the record for fully interlaced mode.  Note that the
     * first three elements contain the three values of the first field, the
     * fourth element contains the value of the second field, and the last two
     * elements contain the two values of the third field.
     */
    for (rec_num = 0; rec_num < N_RECORDS; rec_num++) {
        data_buf1[rec_num][0] = 2; /* easier to inspect the binary file using */
        data_buf1[rec_num][1] = 2; /* a simple value */
        data_buf1[rec_num][2] = 2;
        data_buf1[rec_num][3] = 2;
        data_buf1[rec_num][4] = 2;
        data_buf1[rec_num][5] = 2;
        /*  data_buf1[rec_num][0] = 1 + rec_num;
        data_buf1[rec_num][1] = 2 + rec_num;
        data_buf1[rec_num][2] = 3 + rec_num;
        data_buf1[rec_num][3] = 10 + rec_num;
        data_buf1[rec_num][4] = 10;
        data_buf1[rec_num][5] = 65;
 */
    }

    /* Set the block size and the number of blocks */
    status_n = VSsetblocksize(vsid, BLOCK_SIZE);
    CHECK_VOID(status_n, FAIL, "VSsetblocksize");
    status_n = VSsetnumblocks(vsid, NUM_BLOCKS);
    CHECK_VOID(status_n, FAIL, "VSsetnumblocks");

    /* Write the data from data_buf1 to the non special vdata */
    n_records = VSwrite(vsid, (uint8 *)data_buf1, N_RECORDS, FULL_INTERLACE);
    VERIFY_VOID(n_records, N_RECORDS, "VSwrite");

    n_blocks = VSgetdatainfo(vsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 1, "VSgetdatainfo");

    n_blocks = VSgetdatainfo(vsid, 0, (unsigned)n_blocks, &offset, &length);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");

    /* Verify offset/length */
    VERIFY_VOID(offset, 456, "VSgetdatainfo offset from NONSPECIAL_VS");
    VERIFY_VOID(length, 120, "VSgetdatainfo length from NONSPECIAL_VS");

    status_n = VSdetach(vsid);
    CHECK_VOID(status_n, FAIL, "VSdetach");

    /* Create and write to another simple vdata, named 'Characters Only', in
        class CONTCLASS_NAME */
    vs_ref = VHstoredata(fid, "Only field", (const uint8 *)data_buf2, N_RECORDS, DFNT_CHAR, "Characters Only",
                         CONTCLASS_NAME);
    CHECK_VOID(vs_ref, FAIL, "VHstoredata");

    /* Attach to vdata 'Characters Only' and get offset and length of its data */
    vsid = VSattach(fid, vs_ref, "r");

    n_blocks = VSgetdatainfo(vsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 1, "VSgetdatainfo NONSPECIAL_VS");

    /* Get offset/length */
    n_blocks = VSgetdatainfo(vsid, 0, (unsigned)n_blocks, &offset, &length);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");

    /* Close everything */
    status = VSdetach(vsid);
    CHECK_VOID(status, FAIL, "VSdetach");

    status_n = Vend(fid);
    CHECK_VOID(status_n, FAIL, "Vend");

    status_n = Hclose(fid);
    CHECK_VOID(status_n, FAIL, "Hclose");

    /* Open the file with fopen, read data at the offset obtained and verify
       the values */
    status_n = readnoHDF_char(SIMPLE_FILE, offset, length, data_buf2);
    if (status_n == FAIL)
        fprintf(stderr, "Attempt reading data without HDF4 library failed at line %d\n", __LINE__);
} /* test_simple_vs() */

/****************************************************************************
   Name: test_append_vs() - tests Vdatas with linked-blocks

   Description:
        This routine creates and writes data to an appendable vdata and
        verifies its data information with VSgetdatainfo.  The tests include
        the following:
        - create and write to an appendable vdata
        - create and write to a simple vdata
        - add data to the appendable vdata
        - use VSgetdatainfo to get offsets/lengths of the linked-blocks of data
   BMR - Jul 2010
 ****************************************************************************/
#define APPEND_FILE   "tdatainfo_linkblock.hdf" /* data file */
#define APPENDABLE_VD "Appendable Vdata"
static void
test_append_vs()
{
    int32 fid;                                  /* file ID */
    int32 apvsid;                               /* vdata IDs */
    int32 vs1_ref,                              /* vdata ref# */
        n_records,                              /* number of records written to vdata */
        data_buf0[N_RECORDS][N_VALS_PER_REC_1], /* for "Very Simple Vdata" */
        data_buf1[N_RECORDS][N_VALS_PER_REC_2]; /* for first vdata's data */
    int16            rec_num;                   /* current record number */
    int              n_blocks;
    t_hdf_datainfo_t vs_info;
    int32            status;   /* Status values from routines */
    int              status_n; /* Status values from routines */

    /* Open the HDF file. */
    fid = Hopen(APPEND_FILE, DFACC_CREATE, 0);
    CHECK_VOID(fid, FAIL, "Hopen");

    /* Initialize HDF for subsequent vgroup/vdata access. */
    status_n = Vstart(fid);
    CHECK_VOID(status_n, FAIL, "Vstart");

    /* Create the first vdata */
    apvsid = VSattach(fid, -1, "w");
    CHECK_VOID(apvsid, FAIL, "VSattach");

    vs1_ref = VSQueryref(apvsid);
    CHECK_VOID(vs1_ref, FAIL, "VSQueryref:apvsid");

    status = VSdetach(apvsid);
    CHECK_VOID(status, FAIL, "VSdetach");

    apvsid = VSattach(fid, vs1_ref, "w");
    CHECK_VOID(apvsid, FAIL, "VSattach");

    /* Set name and class name of the vdata. */
    status = VSsetname(apvsid, APPENDABLE_VD);
    CHECK_VOID(status, FAIL, "VSsetname");
    status = VSsetclass(apvsid, LINKED_BLOCK);
    CHECK_VOID(status, FAIL, "VSsetclass");

    n_blocks = VSgetdatainfo(apvsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 0, "VSgetdatainfo");

    /* Introduce each field's name, data type, and order.  This is the first
      part in defining a field.  */
    status_n = VSfdefine(apvsid, FIELD1_NAME, DFNT_INT32, ORDER_1);
    CHECK_VOID(status_n, FAIL, "VSfdefine");
    status_n = VSfdefine(apvsid, FIELD2_NAME, DFNT_INT32, ORDER_2);
    CHECK_VOID(status_n, FAIL, "VSfdefine");
    status_n = VSfdefine(apvsid, FIELD3_NAME, DFNT_INT32, ORDER_3);
    CHECK_VOID(status_n, FAIL, "VSfdefine");

    /* Finalize the definition of the fields. */
    status_n = VSsetfields(apvsid, FIELD_NAME_LIST);
    CHECK_VOID(status_n, FAIL, "VSsetfields");

    /*
     * Buffer the data by the record for fully interlaced mode.  Note that the
     * first three elements contain the three values of the first field, the
     * fourth element contains the value of the second field, and the last two
     * elements contain the two values of the third field.
     */
    for (rec_num = 0; rec_num < N_RECORDS; rec_num++) {
        data_buf1[rec_num][0] = 5; /* easier to inspect the bin file */
        data_buf1[rec_num][1] = 5;
        data_buf1[rec_num][2] = 5;
        data_buf1[rec_num][3] = 5;
        data_buf1[rec_num][4] = 5;
        data_buf1[rec_num][5] = 5;

        /* data_buf1[rec_num][0] = 1 + rec_num;
       data_buf1[rec_num][1] = 2 + rec_num;
       data_buf1[rec_num][2] = 3 + rec_num;
       data_buf1[rec_num][3] = 10 + rec_num;
       data_buf1[rec_num][4] = 10;
       data_buf1[rec_num][5] = 65;
*/
    }

    /* Set the block size and the number of blocks the first vdata */
    status_n = VSsetblocksize(apvsid, BLOCK_SIZE);
    CHECK_VOID(status_n, FAIL, "VSsetblocksize");
    status_n = VSsetnumblocks(apvsid, NUM_BLOCKS);
    CHECK_VOID(status_n, FAIL, "VSsetnumblocks");

    /* Write the data from data_buf1 to vdata APPENDABLE_VD the first time */
    n_records = VSwrite(apvsid, (uint8 *)data_buf1, N_RECORDS, FULL_INTERLACE);
    VERIFY_VOID(n_records, N_RECORDS, "VSwrite");

    n_blocks = VSgetdatainfo(apvsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");

    /* Allocate space to record the vdata's data info */
    if (alloc_info(&vs_info, (unsigned)n_blocks) == -1)
        exit(1);

    /* Get offset and lengths of the data */
    n_blocks = VSgetdatainfo(apvsid, 0, (unsigned)n_blocks, vs_info.offsets, vs_info.lengths);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    free_info(&vs_info);

    /* Get the reference number of this vdata for later use */
    vs1_ref = VSQueryref(apvsid);
    CHECK_VOID(vs1_ref, FAIL, "VSQueryref");

    /* Make another simple vdata to cause linked-blocks */
    for (rec_num = 0; rec_num < N_RECORDS; rec_num++)
        data_buf0[rec_num][0] = 10 + rec_num;

    /* Create and write to another very simple vdata */
    VHstoredata(fid, "Field 1", (const uint8 *)data_buf0, N_RECORDS, DFNT_INT32,
                "Another One Field One Order", "Very Simple Vdata");

    /* Make up the second batch of data for the appendable vdata */
    for (rec_num = 0; rec_num < N_RECORDS; rec_num++) {
        data_buf1[rec_num][0] = 100 + rec_num;
        data_buf1[rec_num][1] = 200 + rec_num;
        data_buf1[rec_num][2] = 300 + rec_num;
        data_buf1[rec_num][3] = 1000 + rec_num;
        data_buf1[rec_num][4] = 1000;
        data_buf1[rec_num][5] = 6500;
    }
    /* Write the data to vdata APPENDABLE_VD the second time */
    n_records = VSwrite(apvsid, (uint8 *)data_buf1, N_RECORDS, FULL_INTERLACE);
    VERIFY_VOID(n_records, N_RECORDS, "VSwrite");

    /* Detach this vdata and attach to it again, just to make sure meta-data
       is recorded; it may not be necessary but it doesn't hurt */
    status = VSdetach(apvsid);
    CHECK_VOID(status, FAIL, "VSdetach");
    apvsid = VSattach(fid, vs1_ref, "w");
    CHECK_VOID(apvsid, FAIL, "VSattach");

    /* Get the number of data blocks the vdata currently has */
    n_blocks = VSgetdatainfo(apvsid, 0, 0, NULL, NULL);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");
    VERIFY_VOID(n_blocks, 3, "VSgetdatainfo");

    /* Allocate space to record the vdata's data info */
    if (alloc_info(&vs_info, (unsigned)n_blocks) == -1)
        exit(1);

    /* Record various info to be used in verifying data later */
    vs_info.n_values = 30;
    vs_info.numtype  = DFNT_INT32;

    /* Get and verify offsets and lengths of data */
    n_blocks = VSgetdatainfo(apvsid, 0, (unsigned)n_blocks, vs_info.offsets, vs_info.lengths);
    CHECK_VOID(n_blocks, FAIL, "VSgetdatainfo");

    {
        int   ii;
        int32 check_offsets[] = {294, 556, 636};
        int32 check_lengths[] = {120, 80, 40}; /* last chunk is not completely filled */
        for (ii = 0; ii < n_blocks; ii++) {
            VERIFY_VOID(vs_info.offsets[ii], check_offsets[ii], "VSgetdatainfo offset");
            VERIFY_VOID(vs_info.lengths[ii], check_lengths[ii], "VSgetdatainfo length");
        }
    }
    /* Verifying data read without HDF4 library */
    /* NOT YET */

    /* Release memory */
    free_info(&vs_info);

    /* Close everything */
    status = VSdetach(apvsid);
    CHECK_VOID(status, FAIL, "Vdetach");
    status = Vend(fid);
    CHECK_VOID(status, FAIL, "Vend");
    status = Hclose(fid);
    CHECK_VOID(status, FAIL, "Hclose");
} /* test_append_vs */

/*******************************************************************
  Name: readnoHDF_char - utility routine to read and verify character
                        data without HDF4 library

  Description:
        readnoHDF_char opens the file and reads in data at the specified
        offset.  The read data is compared against the original data passed
        by caller.  If any mismatch occurs, an error message will be
        displayed but the process will continue.

  Parameters:
        char *filename	IN: name of the file
        int32 offset	IN: where to start read data
        int32 length	IN: how long to read the data

  Return value:
        SUCCEED/FAIL
  BMR - Jul 2010
********************************************************************/
int
readnoHDF_char(const char *filename, const int32 offset, const int32 length, const char *orig_buf)
{
    FILE  *fd;          /* file descriptor */
    size_t readlen = 0; /* number of bytes actually read */
    char  *readcbuf;
    int    ret_value = SUCCEED;

    /* Open the file for reading without SD API */
    fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "readnoHDF_char: unable to open file %s", filename);
        ret_value = FAIL;
    }

    /* Forward to the position of the first block of data */
    if (fseek(fd, (off_t)offset, SEEK_SET) == -1) {
        fprintf(stderr, "readnoHDF_char: unable to seek offset %d\n", (int)offset);
        ret_value = FAIL;
    }

    /* Allocate buffers for SDS' data */
    readcbuf = (char *)malloc((size_t)length * sizeof(char));
    if (readcbuf == NULL) {
        fprintf(stderr, "readnoHDF_char: allocation readcbuf failed\n");
        ret_value = FAIL;
    }

    /* Read in this block of data */
    readlen = fread((void *)readcbuf, 1, (size_t)length, fd);
    if (readlen > 0) {
        /* Compare data read without HDF4 lib against the original buffer */
        if (strncmp(readcbuf, orig_buf, readlen) != 0)
            fprintf(stderr,
                    "Failure: non-HDF reading got different values than written values\n   >>> written = "
                    "%s\n   >>> read = %s\n",
                    orig_buf, readcbuf);
    }
    free(readcbuf);
    /* Close the file */
    if (fclose(fd) == -1) {
        fprintf(stderr, "readnoHDF_char: unable to close file %s", filename);
        ret_value = FAIL;
    }
    return ret_value;
}

/*******************************************************************
  Name: get_annot_datainfo - utility routine to get datainfo of
        an annotation

  Description:
        get_annot_datainfo gets access to each annotation, then attempts to
        get the offset/length of its data.  If successful, increment the
        data info count and eventually returns that number to caller.  If
        failure occurs, simply return FAIL and all the previous data info
        will be discarded by the caller.

  Parameters:
        int32 an_id		IN: annotation ID
        ann_type annot_type	IN: type of the annotations
        int32 num_anns		IN: number of annotations of the specified type,
                                    currently only 1, but will be adjusted after
                                    the last parameter become arrays of strings
        t_ann_info_t *ann_info	OUT: structure that holds ann data info
        int ann_info_num	IN: indicates where in ann_info to start
                                    storing info
        char* ann_text		IN: text of the annotation (this will be changed
                                    to array of strings when time permits)

  Return value:
        The number of annotations whose data info is successfully retrieved
        and stored in ann_info or FAIL if failure occurs.
********************************************************************/
static int
get_annot_datainfo(int32 an_id, ann_type annot_type, int32 num_anns, t_ann_info_t *ann_info, int ann_info_num,
                   const char *ann_text)
{
    int32 ann_id, ann_index;
    int   status_n, ret_value = 0;

    /* Get the annotation. */
    for (ann_index = 0; ann_index < num_anns; ann_index++) {
        /* Get the identifier of the current annotation. */
        ann_id = ANselect(an_id, ann_index, annot_type);
        CHECK(ann_id, FAIL, "ANselect");

        /* Get annotation's data info. */
        status_n = ANgetdatainfo(ann_id, &ann_info[ann_info_num].offset, &ann_info[ann_info_num].length);
        CHECK(status_n, FAIL, "ANgetdatainfo");

        /* Store annotation text for later checking against data read from
           the file without HDF4 library */
        strcpy(ann_info[ann_info_num].anntext, ann_text);

        /* Terminate access to the current annotation. */
        status_n = ANendaccess(ann_id);
        CHECK(status_n, FAIL, "ANendaccess");

        /* Number of annotations whose datainfo is retrieved */
        ret_value++;
    }
    return ret_value;
} /* get_annot_datainfo */

/****************************************************************************
   Name: test_annotation() - tests getting data info of annotations

   Description:
        This routine tests getting data info of file label and description,
        and data label and description.
        - add a file label and a file description
        - create a vgroup then add a data label and a data description to it
        - use the utility routine get_annot_datainfo to record the offset,
          length, and annotation text
        - use utility routine readnoHDF_char to read the annotation text
          without the use of the HDF4 library and verify the correctness of
          the read data

        Todo 2: Should add more annotations so there will be multiple annots
        for an object or file.  get_annot_datainfo needs to be fixed to
        accommodate this.
   BMR - Aug 2010
   NOTE:
        It is near the end of H4 Mapping project and Ruth had said there were
        no annotation for Vgroup and Vdata, so the continuation of these tests
        is not that critical anymore.  For SDS, SDgetanndatainfo handles
        annotations already.  BMR - Jan 2011
 ****************************************************************************/
#define ANNOT_FILE     "tdatainfo_annot.hdf" /* data file */
#define VG_NAME        "AN Vgroup"
#define FILE_LABEL_TXT "General HDF objects"
#define FILE_DESC_TXT  "This is an HDF file that contains general HDF objects"
#define DATA_LABEL_TXT "Common AN Vgroup"
#define DATA_DESC_TXT  "This is a vgroup that is used to test data annotations"
static void
test_annotation()
{
    int32 fid,                           /* file ID */
        an_id,                           /* AN interface ID */
        file_label_id,                   /* file label ID */
        file_desc_id,                    /* file description ID */
        data_label_id,                   /* data label ID */
        data_desc_id,                    /* data description ID */
        vgroup_id;                       /* vgroup ID */
    uint16       vgroup_tag, vgroup_ref; /* vgroup tag/ref */
    t_ann_info_t ann_info[4];            /* temporary storage of annotation info */
    int          status_n;               /* returned status for functions returning an int  */
    int32        status;                 /* returned status for functions returning an int32 */

    /* Create the HDF file. */
    fid = Hopen(ANNOT_FILE, DFACC_CREATE, 0);
    CHECK_VOID(fid, FAIL, "Hopen");

    /* Initialize the AN interface. */
    an_id = ANstart(fid);
    CHECK_VOID(an_id, FAIL, "ANstart");

    /* Create and write a file label. */
    file_label_id = ANcreatef(an_id, AN_FILE_LABEL);
    CHECK_VOID(file_label_id, FAIL, "ANcreatef");
    status = ANwriteann(file_label_id, FILE_LABEL_TXT, strlen(FILE_LABEL_TXT));
    CHECK_VOID(status, FAIL, "ANwriteann");

    /* Create and write a file description. */
    file_desc_id = ANcreatef(an_id, AN_FILE_DESC);
    CHECK_VOID(file_desc_id, FAIL, "ANcreatef");
    status = ANwriteann(file_desc_id, FILE_DESC_TXT, strlen(FILE_DESC_TXT));
    CHECK_VOID(status, FAIL, "ANwriteann");

    /* Create a vgroup to add annotation to it. */
    status_n = Vstart(fid);
    CHECK_VOID(status_n, FAIL, "Vstart");
    vgroup_id = Vattach(fid, -1, "w");
    CHECK_VOID(vgroup_id, FAIL, "Vattach");
    status = Vsetname(vgroup_id, VG_NAME);
    CHECK_VOID(status, FAIL, "Vsetname");

    /* Get the tag and ref number of the vgroup for ANcreate. */
    vgroup_tag = (uint16)VQuerytag(vgroup_id);
    CHECK_VOID(vgroup_tag, 0, "VQuerytag");
    vgroup_ref = (uint16)VQueryref(vgroup_id);
    CHECK_VOID(vgroup_ref, 0, "VQueryref");

    /* Add a data label to the vgroup. */
    data_label_id = ANcreate(an_id, vgroup_tag, vgroup_ref, AN_DATA_LABEL);
    CHECK_VOID(data_label_id, FAIL, "ANcreate");
    status = ANwriteann(data_label_id, DATA_LABEL_TXT, strlen(DATA_LABEL_TXT));
    CHECK_VOID(status, FAIL, "ANwriteann");

    /* Add a data description to the vgroup. */
    data_desc_id = ANcreate(an_id, vgroup_tag, vgroup_ref, AN_DATA_DESC);
    CHECK_VOID(data_desc_id, FAIL, "ANcreate");
    status = ANwriteann(data_desc_id, DATA_DESC_TXT, strlen(DATA_DESC_TXT));
    CHECK_VOID(status, FAIL, "ANwriteann");

    /* Terminate access to each annotation. */
    status_n = ANendaccess(file_label_id);
    CHECK_VOID(status_n, FAIL, "ANendaccess");
    status_n = ANendaccess(file_desc_id);
    CHECK_VOID(status_n, FAIL, "ANendaccess");
    status_n = ANendaccess(data_label_id);
    CHECK_VOID(status_n, FAIL, "ANendaccess");
    status_n = ANendaccess(data_desc_id);
    CHECK_VOID(status_n, FAIL, "ANendaccess");

    /* Terminate access to the vgroup and to the V interface. */
    status   = Vdetach(vgroup_id);
    status_n = Vend(fid);

    /* Terminate access to the AN interface and close the HDF file. */
    status = ANend(an_id);
    CHECK_VOID(status, FAIL, "ANend");
    status_n = Hclose(fid);
    CHECK_VOID(status_n, FAIL, "Hclose");

    /* Open the file and read in location/size of all annotations */

    {
        int32 ann_index,   /* annotation index */
            n_file_labels, /* numbers of file labels */
            n_file_descs,  /* numbers of file descs */
            n_data_labels, /* numbers of data labels */
            n_data_descs;  /* numbers of data descs */
        int num_anns = 0, ann_info_num = 0;

        /* Open the file. */
        fid = Hopen(ANNOT_FILE, DFACC_RDONLY, 0);
        CHECK_VOID(fid, FAIL, "Hopen");

        /* Initialize the AN interface. */
        an_id = ANstart(fid);
        CHECK_VOID(an_id, FAIL, "ANstart");

        /* Get the number of data/file labels/descriptions */
        status_n = ANfileinfo(an_id, &n_file_labels, &n_file_descs, &n_data_labels, &n_data_descs);
        CHECK_VOID(status_n, FAIL, "ANfileinfo");
        VERIFY_VOID(n_file_labels, 1, "ANfileinfo");
        VERIFY_VOID(n_file_descs, 1, "ANfileinfo");
        VERIFY_VOID(n_data_labels, 1, "ANfileinfo");
        VERIFY_VOID(n_data_descs, 1, "ANfileinfo");

        /* Get access to each annotation then call ANgetdatainfo to retrieve
           the offset/length of the annotation data */

        /* Explanation of some parameters to get_annot_datainfo:
           3rd parameter - number of annotations of the specified type,
           currently only 1, but will adjust the last parameter to have arrays
           of character strings when have time.
           4th parameter - ann_info_num indicates where in ann_info to start
           storing info */

        /* AN_DATA_LABEL */
        num_anns = get_annot_datainfo(an_id, AN_DATA_LABEL, 1, ann_info, ann_info_num, DATA_LABEL_TXT);
        CHECK_VOID(num_anns, FAIL, "get_annot_datainfo");
        VERIFY_VOID(num_anns, 1, "get_annot_datainfo");
        ann_info_num = ann_info_num + num_anns;

        /* AN_DATA_DESC */
        num_anns = get_annot_datainfo(an_id, AN_DATA_DESC, 1, ann_info, ann_info_num, DATA_DESC_TXT);
        CHECK_VOID(num_anns, FAIL, "get_annot_datainfo");
        VERIFY_VOID(num_anns, 1, "get_annot_datainfo");
        ann_info_num = ann_info_num + num_anns;

        /* AN_FILE_LABEL */
        num_anns = get_annot_datainfo(an_id, AN_FILE_LABEL, 1, ann_info, ann_info_num, FILE_LABEL_TXT);
        CHECK_VOID(num_anns, FAIL, "get_annot_datainfo");
        VERIFY_VOID(num_anns, 1, "get_annot_datainfo");
        ann_info_num = ann_info_num + num_anns;

        /* AN_FILE_DESC */
        num_anns = get_annot_datainfo(an_id, AN_FILE_DESC, 1, ann_info, ann_info_num, FILE_DESC_TXT);
        CHECK_VOID(num_anns, FAIL, "get_annot_datainfo");
        VERIFY_VOID(num_anns, 1, "get_annot_datainfo");
        ann_info_num = ann_info_num + num_anns;

        /* Terminate access to the AN interface and close the HDF file. */
        status = ANend(an_id);
        CHECK_VOID(status, FAIL, "ANend");
        status_n = Hclose(fid);
        CHECK_VOID(status_n, FAIL, "Hclose");

        /* calling readnoHDF_char to verify data without the use of HDF lib */

        for (ann_index = 0; ann_index < ann_info_num; ann_index++) {
            /* Open the file with fopen, read data at the offset obtained and
               verify the values */
            status_n = readnoHDF_char(ANNOT_FILE, ann_info[ann_index].offset, ann_info[ann_index].length,
                                      ann_info[ann_index].anntext);
            if (status_n == FAIL)
                fprintf(stderr, "Attempt reading data without HDF4 library failed at line %d\n", __LINE__);
        }
    }
} /* test_annotation */

/****************************************************************************
   Name: test_oneblock_ri() - tests non-linked-block images

   Description:
        This routine creates and writes data to images and verifies
        some data and data information with GRgetdatainfo.  The tests include
        the following images:
        - a simple, non-compressed image
        - three images with RLE, Skipping-Huffman, and Deflate compression
        Only the non-compressed image's values are verified against the original
        buffer.  The other three were verified by debugging, additional
        decompression code needed for further verification (Todo 2)

   Note: Incomplete, waiting for schema design
   BMR - Aug 2010
 ****************************************************************************/
#define IMAGE_FILE       "tdatainfo_images.hdf" /* data file */
#define NONCOMP_IMAGE    "Image with No Compression"
#define RLE_IMAGE        "Image with RLE Compression"
#define DEFLATE_IMAGE    "Image with Deflate Compression"
#define SKPHUFF_IMAGE    "Image with Skphuff Compression"
#define JPEG_IMAGE       "Image with JPEG Compression"
#define SKPHUFF_SKIPSIZE 28
#define DEFLATE_LEVEL    7
#define WIDTH            5
#define LENGTH           5
#define N_IMAGES         4

/* Convenient function to create and write to an image, used by
   test_oneblock_ri */
static int
make_comp_image(int32 grid, const char *img_name,
                char       start_char, /* first value in the image, for variety of data */
                int32      comp_type,  /* compression method */
                comp_info *cinfo)      /* compression parameters */
{
    int32 riid;                      /* raster image ID */
    int32 dims[2] = {WIDTH, LENGTH}; /* dimensions for the image */
    char  image0[WIDTH][LENGTH];     /* image data */
    int32 start[2];                  /* start of image data to grab */
    int32 stride[2];                 /* stride of image data to grab */
    int   ii, jj;                    /* indices */
    int   status;                    /* generic return value */
    int   ret_value = SUCCEED;

    /* Initialize data we are going to write out, each image created by this
       convenient function will use the same data pattern with the first value
       given by the caller  */
    for (ii = 0; ii < WIDTH; ii++)
        for (jj = 0; jj < LENGTH; jj++)
            image0[ii][jj] = start_char + (char)jj;

    /* Create the image with 1 component, type char, pixel interlace, and
       dimension WIDTHxLENGTH */
    riid = GRcreate(grid, img_name, 1, DFNT_CHAR, MFGR_INTERLACE_PIXEL, dims);
    CHECK(riid, FAIL, "GRcreate");

    /* Set compression as instructed */
    if (comp_type != COMP_CODE_NONE) {
        status = GRsetcompress(riid, comp_type, cinfo);
        CHECK(status, FAIL, "GRsetcompress");
    }

    /* Write the entire image data out */
    start[0] = start[1] = 0;
    stride[0] = stride[1] = 1;
    status                = GRwriteimage(riid, start, stride, dims, image0);
    CHECK(status, FAIL, "GRreadimage");

    /* Close the first image */
    status = GRendaccess(riid);
    CHECK(status, FAIL, "GRendaccess");

    return ret_value;
}

static void
test_oneblock_ri()
{
    int32 fid, grid,          /* file ID and GR interface ID */
        riid;                 /* raster image ID */
    int32     offset, length; /* offset/length buffers for single block of data */
    int       status;         /* status returned from routines */
    int       ii;             /* indices */
    int32     n_images, n_fattrs;
    comp_info cinfo; /* Compression parameters - union */
    /* offsets/lengths to be used to verify offsets/lengths returned by
       GRgetdatainfo - confirmed by the command "od" on the hdf file */
    static int32 image_data_offsets[N_IMAGES] = {309, 363, 426, 486};
    static int32 image_data_lengths[N_IMAGES] = {25, 26, 29, 16};

    /****************************************************************
      Create a file in GR interface then create and write several
      images with and without compression.
    ****************************************************************/

    /* Create the HDF file and initialize the interface. */
    fid = Hopen(IMAGE_FILE, DFACC_CREATE, 0);
    CHECK_VOID(fid, FAIL, "Hopen");

    grid = GRstart(fid);
    CHECK_VOID(grid, FAIL, "GRstart");

    /* Create and write the non-compressed image to this file */
    status = make_comp_image(grid, NONCOMP_IMAGE, 'n', COMP_CODE_NONE, &cinfo);

    /* Create and write 3 more images: RLE, Deflate, and Skipping Huffman */

    /* No compression info for the RLE image */
    memset(&cinfo, 0, sizeof(cinfo));

    /* Create and write the RLE compressed image to this file, starting the
       data values with the letter 'r' */
    status = make_comp_image(grid, RLE_IMAGE, 'r', COMP_CODE_RLE, &cinfo);

    /* Set the compression info for the image with Skipping Huffman method */
    memset(&cinfo, 0, sizeof(cinfo));
    cinfo.skphuff.skp_size = SKPHUFF_SKIPSIZE;

    /* Create and write the Skipping Huffman compressed image to this file,
       starting the data values with the letter 's' */
    status = make_comp_image(grid, SKPHUFF_IMAGE, 's', COMP_CODE_SKPHUFF, &cinfo);

    /* Set the compression info for the image with Deflate method */
    memset(&cinfo, 0, sizeof(cinfo));
    cinfo.deflate.level = DEFLATE_LEVEL;

    /* Create and write the Deflate compressed image to this file, starting the
       data values with the letter 'd' */
    status = make_comp_image(grid, DEFLATE_IMAGE, 'd', COMP_CODE_DEFLATE, &cinfo);

    /* Set the compression method for the image with JPEG method */
    memset(&cinfo, 0, sizeof(cinfo));
    cinfo.jpeg.quality        = 100;
    cinfo.jpeg.force_baseline = 1;

    /* Create and write the JPEG compressed image to this file, starting the
       data values with the letter 'j' - more work to be done for JPEG */
    /* status = make_comp_image(grid, JPEG_IMAGE, 'j', COMP_CODE_JPEG, &cinfo);
     */

    /* Terminate access to the GR interface and close the file */
    status = GRend(grid);
    CHECK_VOID(status, FAIL, "GRend");
    status = Hclose(fid);
    CHECK_VOID(status, FAIL, "Hclose");

    /****************************************************************
      Re-open the file to read the images and their data information
    ****************************************************************/

    /* Re-open the file and initialize the GR interface */
    fid = Hopen(IMAGE_FILE, DFACC_RDONLY, 0);
    CHECK_VOID(fid, FAIL, "Hopen");
    grid = GRstart(fid);
    CHECK_VOID(grid, FAIL, "GRstart");

    /* Get the number of images in the file */
    status = GRfileinfo(grid, &n_images, &n_fattrs);
    CHECK_VOID(status, FAIL, "GRfileinfo");
    VERIFY_VOID(n_images, N_IMAGES, "GRfileinfo");

    /* Open each image then get and verify its data information.  Note that
        currently, the offsets and lengths are obtained from debugging
        and the command od on the file */
    for (ii = 0; ii < n_images; ii++) {
        riid = GRselect(grid, ii);
        CHECK_VOID(riid, FAIL, "GRselect");

        status = GRgetdatainfo(riid, 0, 1, &offset, &length);
        CHECK_VOID(status, FAIL, "GRgetdatainfo");
        VERIFY_VOID(offset, image_data_offsets[ii], "GRgetdatainfo");
        VERIFY_VOID(length, image_data_lengths[ii], "GRgetdatainfo");

        /* Only verify data of the first image, which has non-compressed data. */
        if (ii == 0) {
            int  jj, kk;
            char buffer[WIDTH][LENGTH], check_image[WIDTH * LENGTH];
            for (kk = 0; kk < WIDTH; kk++)
                for (jj = 0; jj < LENGTH; jj++)
                    buffer[kk][jj] = 'n' + (char)jj;

            /* Work around to pass check_image into readnoHDF_char w/o warning*/
            memcpy(check_image, buffer, WIDTH * LENGTH);

            /* Open the file with fopen, read data at the offset obtained and verify
               the values */
            status = readnoHDF_char(IMAGE_FILE, offset, length, check_image);
            if (status == FAIL)
                fprintf(stderr, "Attempt reading data without HDF4 library failed at line %d\n", __LINE__);
        }

        /* Close the image */
        status = GRendaccess(riid);
        CHECK_VOID(status, FAIL, "GRendaccess");
    } /* for n_images */

    /* Terminate access to the GR interface and close the file */
    status = GRend(grid);
    CHECK_VOID(status, FAIL, "GRend");
    status = Hclose(fid);
    CHECK_VOID(status, FAIL, "Hclose");
} /* end test_oneblock_ri */

#define IMAGE_DF_FILE "tdatainfo_dfri.hdf" /* data file for DFR APIs */
#define N_DF_IMAGES                                                                                          \
    2 /* number of DF images in the file, 1 RI8 &                                                            \
         1 RI24 */
static void
test_dfr8_24()
{
    int32 fid, grid,      /* file ID and GR interface ID */
        riid;             /* raster image ID */
    int32 offset, length; /* offset/length buffers for single block of data */
    int   status;         /* status returned from routines */
    int   ii, jj;         /* indices */
    int32 n_images, n_fattrs;
    char  buf[WIDTH][LENGTH][3];

    /* offsets/lengths to be used to verify offsets/lengths returned by
       GRgetdatainfo - confirmed by the command "od" on the hdf file */
    static int32 image_data_offsets[N_IMAGES] = {294, 1132};
    static int32 image_data_lengths[N_IMAGES] = {30, 75};

    /* Initialize the 8-bit image array */
    static uint8 raster_data[WIDTH][LENGTH] = {
        {1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}, {1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}, {6, 4, 2, 0, 2}};

    /* Write the 8-bit raster image to file */
    status = DFR8putimage(IMAGE_DF_FILE, raster_data, WIDTH, LENGTH, COMP_RLE);

    /* Initialize the 24-bit image array */
    for (ii = 0; ii < WIDTH; ii++)
        for (jj = 0; jj < LENGTH; jj++) {
            buf[ii][jj][0] = buf[ii][jj][1] = buf[ii][jj][2] = (char)(ii + jj);
        }

    /* Set interlace for the 24-bit RI */
    status = DF24setil(DFIL_PIXEL);

    /* Make sure that no compression is being used - the variable Grcompr is
       global in dfgr.c so its value is inherited from other tests in the lib
       and causes this image to be compressed with JPEG unintentionally.  We
       want to be able to see the data to verify it with "od" */
    status = DF24setcompress(COMP_NONE, NULL);

    /* Write the 24-bit raster image to file */
    status = DF24addimage(IMAGE_DF_FILE, &(buf[0][0][0]), WIDTH, LENGTH);

    /****************************************************************
      Re-open the file to read the images and their data information
     ****************************************************************/
    /* Re-open the file and initialize the GR interface */
    fid = Hopen(IMAGE_DF_FILE, DFACC_RDONLY, 0);
    CHECK_VOID(fid, FAIL, "Hopen");
    grid = GRstart(fid);
    CHECK_VOID(grid, FAIL, "GRstart");

    /* Get the number of images in the file */
    status = GRfileinfo(grid, &n_images, &n_fattrs);
    CHECK_VOID(status, FAIL, "GRfileinfo");
    VERIFY_VOID(n_images, N_DF_IMAGES, "GRfileinfo");

    /* Open each image then get and verify its data information.  Note that
        currently, the offsets and lengths are obtained from debugging
        and the command od on the file */
    for (ii = 0; ii < n_images; ii++) {
        int info_count = FAIL;

        riid = GRselect(grid, ii);
        CHECK_VOID(riid, FAIL, "GRselect");

        /* Get the number of data blocks and verify; should be 1 */
        info_count = GRgetdatainfo(riid, 0, 0, NULL, NULL);
        CHECK_VOID(info_count, FAIL, "GRgetdatainfo");
        VERIFY_VOID(info_count, 1, "GRgetdatainfo");

        /* Get offset/length of the image and verify with pre-determined
           values */
        info_count = GRgetdatainfo(riid, 0, (unsigned)info_count, &offset, &length);
        CHECK_VOID(info_count, FAIL, "GRgetdatainfo");
        VERIFY_VOID(offset, image_data_offsets[ii], "GRgetdatainfo");
        VERIFY_VOID(length, image_data_lengths[ii], "GRgetdatainfo");

        /* Close the image */
        status = GRendaccess(riid);
        CHECK_VOID(status, FAIL, "GRendaccess");
    } /* for n_images */

    /* Terminate access to the GR interface and close the file */
    status = GRend(grid);
    CHECK_VOID(status, FAIL, "GRend");
    status = Hclose(fid);
    CHECK_VOID(status, FAIL, "Hclose");
} /* test_dfr8_24 */

/*************************************************************************
 test_getpalinfo() - tests GRgetpalinfo
 Need documentation
**************************************************************************/
#define IMAGE_DFPAL_FILE "tdatainfo_pal.hdf"
#define IMAGE_WITH_PAL   "GR Image with Palette"
#define IMAGE2_WITH_PAL  "Second GR Image w/pal"
#define ANO_IMAGE_NAME   "ANO_IMAGE_NAME"
#define LASTIMAGE_NOPAL  "Last GR Image: no pal"
#define N_COMPS_IMG      2   /* number of image components */
#define N_ENTRIES        256 /* number of entries in the palette */
#define N_COMPS_PAL      3   /* number of palette's components */

static void
test_getpalinfo()
{
    int32 fid, grid,                             /* file ID and GR interface ID */
        riid, palid,                             /* raster image ID and palette ID */
        interlace_mode, start[2],                /* where to start to write for each dimension  */
        edges[2],                                /* specifies how long to write for each dimension */
        dim_sizes[2];                            /* sizes of the two dimensions of the image array */
    uint8 image_buf[WIDTH][LENGTH][N_COMPS_IMG]; /* data of raster image */
    uint8 palette_buf1[N_ENTRIES][N_COMPS_PAL];  /* for LUT mostly */
    uint8 palette_buf2[N_ENTRIES][N_COMPS_PAL];
    uint8 paletteA[N_ENTRIES * N_COMPS_PAL] = {0}; /* for IP8 mostly */
    uint8 paletteB[N_ENTRIES * N_COMPS_PAL] = {0};
    uint8 paletteD[N_ENTRIES * N_COMPS_PAL] = {0};
    int   n_pals = 0; /* number of palettes, returned by DFPnpals and GRgetpalinfo */

    hdf_ddinfo_t *palinfo_array = NULL; /* list of palette DDs */
    uint8        *inbuf;                /* palette data read back in */
    int           ii, jj;               /* indices */
    int           status;               /* status returned from routines */

    /* Initialize the 8-bit image array */
    static uint8 raster_data[WIDTH][LENGTH] = {
        {1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}, {1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}, {6, 4, 2, 0, 2}};

    /* Palettes are added in the following means and order:
        paletteA (DFPputpal)
        paletteB (DFPputpal)
        paletteA (DFR8setpalette/DFR8addimage)
        palette_buf1 (GRwritelut) for image named IMAGE_WITH_PAL
        palette_buf2 (GRwritelut) for image named IMAGE2_WITH_PAL
        paletteB (DFR8setpalette/DFR8addimage)
        paletteD (DFPputpal)
        paletteB (DFPputpal)
        paletteD (DFPputpal)
        palette_buf2 (GRwritelut) for image named IMAGE2_WITH_PAL
        paletteD (DFPputpal)
    */
    /* Add two palettes with DFP API. */
    status = DFPputpal(IMAGE_DFPAL_FILE, paletteA, 0, "w");
    CHECK_VOID(status, FAIL, "DFPputpal");

    status = DFPputpal(IMAGE_DFPAL_FILE, paletteB, 0, "a");
    CHECK_VOID(status, FAIL, "DFPputpal");

    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 2, "DFPputpal"); /* 2 palettes from 2 DFPputpal's */

    /* Specify palette to be used with subsequent 8-bit images */
    status = DFR8setpalette(paletteA);
    CHECK_VOID(status, FAIL, "DFR8setpalette");

    /* Write an 8-bit raster image to the file */
    status = DFR8addimage(IMAGE_DFPAL_FILE, raster_data, WIDTH, LENGTH, COMP_RLE);
    CHECK_VOID(status, FAIL, "DFR8addimage");

    /* Get the number of palettes using DFP API */
    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 3, "DFPputpal");
    /* 3 palettes: 2 DFPputpal's + DFR8setpalette/DFR8addimage combo */

    /* Write another 8-bit raster image to file, without calling another
       DFR8setpalette, that means this image is using the same palette as the
       previous image.  This is when only 201 is created */
    status = DFR8addimage(IMAGE_DFPAL_FILE, raster_data, WIDTH, LENGTH, COMP_RLE);
    CHECK_VOID(status, FAIL, "DFR8addimage");

    /* Thus, the number of palettes returned by DFPnpals should be the same as
       from the last call to DFPnpals */
    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 3, "DFPputpal");

    /****************************************************************
        Re-open the file in GR interface, add a few images with
        palettes, then test GRgetpalinfo on the palettes
     ****************************************************************/

    /* Re-open the file and initialize the GR interface */
    fid = Hopen(IMAGE_DFPAL_FILE, DFACC_RDWR, 0);
    CHECK_VOID(fid, FAIL, "Hopen");
    grid = GRstart(fid);
    CHECK_VOID(grid, FAIL, "GRstart");

    /* Define the dimensions and interlace mode of the image */
    dim_sizes[0]   = LENGTH;
    dim_sizes[1]   = WIDTH;
    interlace_mode = MFGR_INTERLACE_PIXEL;

    /* Create an image named IMAGE_WITH_PAL */
    riid = GRcreate(grid, IMAGE_WITH_PAL, N_COMPS_IMG, DFNT_UINT8, interlace_mode, dim_sizes);

    /* Fill the image data buffer with values */
    for (ii = 0; ii < WIDTH; ii++) {
        for (jj = 0; jj < LENGTH; jj++) {
            image_buf[ii][jj][0] = (uint8)(ii + jj) + 1;
            image_buf[ii][jj][1] = (uint8)(ii + jj) + 2;
        }
    }

    /* Define the size of the data to be written */
    start[0] = start[1] = 0;
    edges[0]            = WIDTH;
    edges[1]            = LENGTH;

    /* Write the data in the buffer into the image array */
    status = GRwriteimage(riid, start, NULL, edges, (void *)image_buf);

    /* Initialize the palette data */
    for (ii = 0; ii < N_ENTRIES; ii++) {
        palette_buf1[ii][0] = (uint8)ii;
        palette_buf1[ii][1] = 0;
        palette_buf1[ii][2] = 8;
    }

    /* Get the identifier of the palette attached to the image IMAGE_WITH_PAL */
    palid = GRgetlutid(riid, 0);

    /* Write data to the palette. */
    status = GRwritelut(palid, N_COMPS_PAL, DFNT_UINT8, interlace_mode, N_ENTRIES, (void *)palette_buf1);

    /* DFPnpals now sees another palette */
    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 4, "DFPputpal");

    /* Terminate access to the first image */
    status = GRendaccess(riid);

    /* Create another image named IMAGE2_WITH_PAL */
    riid = GRcreate(grid, IMAGE2_WITH_PAL, N_COMPS_IMG, DFNT_UINT8, interlace_mode, dim_sizes);

    /* Write the data in the buffer into the image array */
    status = GRwriteimage(riid, start, NULL, edges, (void *)image_buf);

    /* Get the id of the palette attached to the image IMAGE2_WITH_PAL */
    palid = GRgetlutid(riid, 0);

    /* Initialize the palette for second image */
    for (ii = 0; ii < N_ENTRIES; ii++) {
        palette_buf2[ii][0] = 2;
        palette_buf2[ii][1] = 4;
        palette_buf2[ii][2] = 8;
    }

    /* Write data to the palette */
    status = GRwritelut(palid, N_COMPS_PAL, DFNT_UINT8, interlace_mode, N_ENTRIES, (void *)palette_buf2);

    /* DFPnpals now sees another palette */
    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 5, "DFPputpal");

    /* Terminate access to this image */
    status = GRendaccess(riid);
    CHECK_VOID(status, FAIL, "GRendaccess");

    /* Create another image named LASTIMAGE_NOPAL */
    riid = GRcreate(grid, LASTIMAGE_NOPAL, N_COMPS_IMG, DFNT_UINT8, interlace_mode, dim_sizes);
    CHECK_VOID(riid, FAIL, "GRcreate");

    /* Write the data in the buffer into the image array */
    status = GRwriteimage(riid, start, NULL, edges, (void *)image_buf);
    CHECK_VOID(status, FAIL, "GRwriteimage");

    /* Terminate access to the image */
    status = GRendaccess(riid);
    CHECK_VOID(status, FAIL, "GRendaccess");

    status = DFR8setpalette(paletteB);
    CHECK_VOID(status, FAIL, "DFR8setpalette");

    /* Write another 8-bit raster image to file */
    status = DFR8addimage(IMAGE_DFPAL_FILE, raster_data, WIDTH, LENGTH, COMP_RLE);
    CHECK_VOID(status, FAIL, "DFR8addimage");

    /* DFR8setpalette/DFR8addimage just added another palette, so DFPnpals now
       returns 6 */
    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 6, "DFPputpal");

    status = DFPputpal(IMAGE_DFPAL_FILE, paletteD, 0, "a");
    CHECK_VOID(status, FAIL, "DFPputpal");

    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 7, "DFPnpals");

    status = DFPputpal(IMAGE_DFPAL_FILE, paletteB, 0, "a");
    CHECK_VOID(status, FAIL, "DFPputpal");

    status = DFPputpal(IMAGE_DFPAL_FILE, paletteD, 0, "a");
    CHECK_VOID(status, FAIL, "DFPputpal");

    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 9, "DFPputpal");

    /* Create another image named ANO_IMAGE_NAME. */
    riid = GRcreate(grid, ANO_IMAGE_NAME, N_COMPS_IMG, DFNT_UINT8, interlace_mode, dim_sizes);
    CHECK_VOID(riid, FAIL, "GRcreate");

    /* Write the data in the buffer into the image array. */
    status = GRwriteimage(riid, start, NULL, edges, (void *)image_buf);
    CHECK_VOID(status, FAIL, "GRwriteimage");

    /* Get the identifier of the palette attached to the image ANO_IMAGE_NAME */
    palid = GRgetlutid(riid, 0);
    CHECK_VOID(palid, FAIL, "GRgetlutid");

    /* Write data to the palette. */
    status = GRwritelut(palid, N_COMPS_PAL, DFNT_UINT8, interlace_mode, N_ENTRIES, (void *)palette_buf2);

    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 10, "DFPputpal");

    status = GRendaccess(riid);

    status = DFPputpal(IMAGE_DFPAL_FILE, paletteD, 0, "a");
    CHECK_VOID(status, FAIL, "DFPputpal");

    n_pals = DFPnpals(IMAGE_DFPAL_FILE);
    CHECK_VOID(n_pals, FAIL, "DFPnpals");
    VERIFY_VOID(n_pals, 11, "DFPputpal");

    /* Assuming that this file has been written exactly in this manner, this
       is what the palette DDs would look like at this point:
        tag    ref    offset  length
        201    1      294       768
        301    1      294       768
        201    2      1062      768
        301    2      1062      768
        301    3      1860      768
        201    3      1860      768
        201    4      1860      768
        301    4      2973      768 <- cannot be read by DFPgetpal
        301    5      3806      768 <- cannot be read by DFPgetpal
        301    8      4669      768
        201    8      4669      768
        201    5      5449      768
        201    6      6217      768
        301    6      6217      768
        201    7      7183      768
        301    7      7183      768
        301    9      8016      768 <- cannot be read by DFPgetpal
        201    9      8784      768
 */

    /* Read some palettes */
    {

        /* Call GRgetpalinfo the first time, passing in NULL for the palette array,
           to get the number of palettes in the file */
        n_pals = 0;
        n_pals = GRgetpalinfo(grid, 0, NULL);
        CHECK_VOID(n_pals, FAIL, "GRgetpalinfo");

        palinfo_array = (hdf_ddinfo_t *)malloc((size_t)n_pals * sizeof(hdf_ddinfo_t));
        CHECK_ALLOC(palinfo_array, "palinfo_array", "test_getpalinfo");

        n_pals = GRgetpalinfo(grid, (unsigned)n_pals, palinfo_array);
        CHECK_VOID(n_pals, FAIL, "GRgetpalinfo");

        /* Read and verify data of the first palette which is pointed to by both
           data identifiers 201/ref and 301/ref */
        /* inbuf = (uint8 *) malloc(palinfo_array[0].length * sizeof(uint8));
         */
        inbuf = (uint8 *)malloc((size_t)(palinfo_array[0].length));
        CHECK_ALLOC(inbuf, "inbuf", "test_getpalinfo");
        status = Hgetelement(fid, palinfo_array[0].tag, palinfo_array[0].ref, inbuf);
        CHECK_VOID(status, FAIL, "Hgetelement");

        if (memcmp(inbuf, paletteA, (size_t)(palinfo_array[0].length)) != 0)
            fprintf(stderr,
                    "palette data pointed by tag/ref = %d/%d at offset/length = %d/%d differs from written\n",
                    palinfo_array[0].tag, palinfo_array[0].ref, palinfo_array[0].offset,
                    palinfo_array[0].length);
        free(inbuf);

        /* Read and verify data of the palette pointed to by 301/4.  This is the
           data element that was not revealed by DFPgetpal because the tag/ref pair
           201/4 is associated with a different offset */

        /* inbuf = (uint8 *) malloc(palinfo_array[7].length * sizeof(uint8));
         */
        inbuf = (uint8 *)malloc((size_t)palinfo_array[7].length);
        CHECK_ALLOC(inbuf, "inbuf", "test_getpalinfo");
        status = Hgetelement(fid, palinfo_array[7].tag, palinfo_array[7].ref, inbuf);
        CHECK_VOID(status, FAIL, "Hgetelement");

        if (memcmp(inbuf, palette_buf1, (size_t)palinfo_array[7].length) != 0)
            fprintf(stderr,
                    "palette data pointed by tag/ref = %d/%d at offset/length = %d/%d differs from written\n",
                    palinfo_array[7].tag, palinfo_array[7].ref, palinfo_array[7].offset,
                    palinfo_array[7].length);
        free(inbuf);

        free(palinfo_array);
    }

    /* Terminate access to the GR interface and close the file */
    status = GRend(grid);
    CHECK_VOID(status, FAIL, "GRend");
    status = Hclose(fid);
    CHECK_VOID(status, FAIL, "Hclose");

} /* test_getpalinfo */

/****************************************************************************
   Name: test_getntinfo() - tests getting number type's information

   Description:
        This routine simply calls Hgetntinfo with various types and verifies
        the information retrieved.
   BMR - Aug 2010
 ****************************************************************************/
static void
test_getntinfo()
{
    hdf_ntinfo_t nt_info;
    int          status = SUCCEED;

    status = Hgetntinfo(DFNT_UINT8, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_UINT8");
    VERIFY_CHAR_VOID(nt_info.type_name, "uint8", "Hgetntinfo DFNT_UINT8");
    VERIFY_CHAR_VOID(nt_info.byte_order, "bigEndian", "Hgetntinfo DFNT_UINT8");

    status = Hgetntinfo(DFNT_CHAR16, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_CHAR16");
    VERIFY_CHAR_VOID(nt_info.type_name, "char16", "Hgetntinfo DFNT_CHAR16");
    VERIFY_CHAR_VOID(nt_info.byte_order, "bigEndian", "Hgetntinfo DFNT_CHAR16");

    /* Native */
    status = Hgetntinfo(DFNT_NFLOAT32, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_NFLOAT32");
    VERIFY_CHAR_VOID(nt_info.type_name, "float32", "Hgetntinfo DFNT_NFLOAT32");
    VERIFY_CHAR_VOID(nt_info.byte_order, "bigEndian", "Hgetntinfo DFNT_NFLOAT32");

    /* Little endian */
    status = Hgetntinfo(DFNT_LFLOAT32, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_LFLOAT32");
    VERIFY_CHAR_VOID(nt_info.type_name, "float32", "Hgetntinfo DFNT_LFLOAT32");
    VERIFY_CHAR_VOID(nt_info.byte_order, "littleEndian", "Hgetntinfo DFNT_LFLOAT32");

    /* Little endian backward compatible */
    status = Hgetntinfo(DFNT_LCHAR, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_LCHAR");
    VERIFY_CHAR_VOID(nt_info.type_name, "char8", "Hgetntinfo DFNT_LCHAR");
    VERIFY_CHAR_VOID(nt_info.byte_order, "littleEndian", "Hgetntinfo DFNT_LCHAR");

    /* Backward compatible */
    status = Hgetntinfo(DFNT_DOUBLE, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_DOUBLE");
    VERIFY_CHAR_VOID(nt_info.type_name, "float64", "Hgetntinfo DFNT_DOUBLE");
    VERIFY_CHAR_VOID(nt_info.byte_order, "bigEndian", "Hgetntinfo DFNT_DOUBLE");

    /* Native backward compatible */
    status = Hgetntinfo(DFNT_NUCHAR, &nt_info);
    CHECK_VOID(status, FAIL, "Hgetntinfo DFNT_NUCHAR");
    VERIFY_CHAR_VOID(nt_info.type_name, "uchar8", "Hgetntinfo DFNT_NUCHAR");
    VERIFY_CHAR_VOID(nt_info.byte_order, "bigEndian", "Hgetntinfo DFNT_NUCHAR");
} /* test_getntinfo */

/* Test driver for testing the public functions VSgetdatainfo, ANgetdatainfo,
   GRgetdatainfo, and Hgetntinfo. */
void
test_datainfo()
{
    /* Test VSgetdatainfo with data stored in one contiguous block */
    test_simple_vs();

    /* Test VSgetdatainfo with data might be stored in linked blocks */
    test_append_vs();

    /* Test ANgetdatainfo */
    test_annotation();

    /* Test GRgetdatainfo with images stored in one contiguous block, with
       and without compression */
    test_oneblock_ri();

    /* Test GRgetdatainfo with RI8 and RI24 */
    test_dfr8_24();

    /* Test GRgetpalinfo with RI8 and GR */
    test_getpalinfo();

    /* Test Hgetntinfo */
    test_getntinfo();
}
